أطلق العنان لقوة WebCodecs! دليل شامل للوصول إلى بيانات إطار الفيديو ومعالجتها باستخدام مستويات VideoFrame. تعلم تنسيقات البكسل، وتخطيط الذاكرة، وحالات الاستخدام العملي لمعالجة الفيديو المتقدمة في المتصفح.
مستويات WebCodecs VideoFrame: نظرة عميقة على الوصول إلى بيانات إطار الفيديو
تمثل WebCodecs نقلة نوعية في معالجة الوسائط على الويب. فهي توفر وصولاً منخفض المستوى إلى اللبنات الأساسية للوسائط، مما يمكّن المطورين من إنشاء تطبيقات متطورة مباشرة في المتصفح. إحدى أقوى ميزات WebCodecs هي كائن VideoFrame، وداخله، مستويات VideoFrame التي تكشف بيانات البكسل الخام لإطارات الفيديو. يقدم هذا المقال دليلاً شاملاً لفهم واستخدام مستويات VideoFrame لمعالجة الفيديو المتقدمة.
فهم كائن VideoFrame
قبل الغوص في المستويات، دعونا نلخص كائن VideoFrame نفسه. يمثل VideoFrame إطارًا واحدًا من الفيديو. إنه يغلف بيانات الفيديو التي تم فك ترميزها (أو ترميزها)، إلى جانب البيانات الوصفية المرتبطة بها مثل الطابع الزمني، والمدة، ومعلومات التنسيق. توفر واجهة برمجة تطبيقات VideoFrame طرقًا من أجل:
- قراءة بيانات البكسل: هذا هو المكان الذي تظهر فيه المستويات.
- نسخ الإطارات: إنشاء كائنات
VideoFrameجديدة من الكائنات الحالية. - إغلاق الإطارات: تحرير الموارد الأساسية التي يحتفظ بها الإطار.
يتم إنشاء كائن VideoFrame أثناء عملية فك الترميز، عادةً بواسطة VideoDecoder، أو يدويًا عند إنشاء إطار مخصص.
ما هي مستويات VideoFrame؟
غالبًا ما يتم تنظيم بيانات البكسل الخاصة بـ VideoFrame في مستويات متعددة، خاصة في تنسيقات مثل YUV. يمثل كل مستوى مكونًا مختلفًا من الصورة. على سبيل المثال، في تنسيق YUV420، هناك ثلاثة مستويات:
- Y (Luma): يمثل سطوع الصورة (luminance). يحتوي هذا المستوى على معلومات التدرج الرمادي.
- U (Cb): يمثل مكون اللونية لفرق اللون الأزرق.
- V (Cr): يمثل مكون اللونية لفرق اللون الأحمر.
تنسيقات RGB، على الرغم من أنها تبدو أبسط، قد تستخدم أيضًا مستويات متعددة في بعض الحالات. يعتمد عدد المستويات ومعناها بالكامل على VideoPixelFormat الخاص بـ VideoFrame.
تكمن ميزة استخدام المستويات في أنها تسمح بالوصول الفعال إلى مكونات لونية محددة ومعالجتها. على سبيل المثال، قد ترغب في تعديل السطوع فقط (مستوى Y) دون التأثير على اللون (مستويي U و V).
الوصول إلى مستويات VideoFrame: واجهة برمجة التطبيقات (API)
توفر واجهة برمجة تطبيقات VideoFrame الطرق التالية للوصول إلى بيانات المستوى:
copyTo(destination, options): تنسخ محتوىVideoFrameإلى وجهة، والتي يمكن أن تكونVideoFrameآخر، أوCanvasImageBitmap، أوArrayBufferView. يتحكم كائنoptionsفي المستويات التي يتم نسخها وكيفية ذلك. هذه هي الآلية الأساسية للوصول إلى المستوى.
يسمح لك كائن options في طريقة copyTo بتحديد التخطيط والهدف لبيانات إطار الفيديو. تشمل الخصائص الرئيسية:
format: تنسيق البكسل المطلوب للبيانات المنسوخة. يمكن أن يكون هذا هو نفسه تنسيقVideoFrameالأصلي أو تنسيق مختلف (على سبيل المثال، التحويل من YUV إلى RGB).codedWidthوcodedHeight: عرض وارتفاع إطار الفيديو بالبكسل.layout: مصفوفة من الكائنات تصف تخطيط كل مستوى في الذاكرة. يحدد كل كائن في المصفوفة:offset: الإزاحة، بالبايت، من بداية مخزن البيانات المؤقت إلى بداية بيانات المستوى.stride: عدد البايتات بين بداية كل صف في المستوى. هذا أمر حاسم للتعامل مع الحشو (padding).
دعونا نلقي نظرة على مثال لنسخ VideoFrame بتنسيق YUV420 إلى مخزن بيانات خام:
async function copyYUV420ToBuffer(videoFrame, buffer) {
const width = videoFrame.codedWidth;
const height = videoFrame.codedHeight;
// YUV420 has 3 planes: Y, U, and V
const yPlaneSize = width * height;
const uvPlaneSize = width * height / 4;
const layout = [
{ offset: 0, stride: width }, // Y plane
{ offset: yPlaneSize, stride: width / 2 }, // U plane
{ offset: yPlaneSize + uvPlaneSize, stride: width / 2 } // V plane
];
await videoFrame.copyTo(buffer, {
format: 'I420',
codedWidth: width,
codedHeight: height,
layout: layout
});
videoFrame.close(); // Important to release resources
}
الشرح:
- نحسب حجم كل مستوى بناءً على
widthوheight. مستوى Y بدقة كاملة، بينما يتم تقليل عينات U و V (4:2:0). - تحدد مصفوفة
layoutتخطيط الذاكرة. تحددoffsetمكان بدء كل مستوى في المخزن المؤقت، وتحددstrideعدد البايتات التي يجب القفز بها للوصول إلى الصف التالي في ذلك المستوى. - تم تعيين خيار
formatإلى 'I420'، وهو تنسيق YUV420 شائع. - بشكل حاسم، بعد النسخ، يتم استدعاء
videoFrame.close()لتحرير الموارد.
تنسيقات البكسل: عالم من الإمكانيات
يعد فهم تنسيقات البكسل أمرًا ضروريًا للعمل مع مستويات VideoFrame. يحدد VideoPixelFormat كيفية ترميز معلومات اللون داخل إطار الفيديو. إليك بعض تنسيقات البكسل الشائعة التي قد تواجهها:
- I420 (YUV420p): تنسيق YUV مستوٍ حيث يتم تخزين مكونات Y و U و V في مستويات منفصلة. يتم تقليل عينات U و V بمعامل 2 في كل من الأبعاد الأفقية والعمودية. إنه تنسيق شائع جدًا وفعال.
- NV12 (YUV420sp): تنسيق YUV شبه مستوٍ حيث يتم تخزين Y في مستوى واحد، ومكونات U و V متشابكة في مستوى ثانٍ.
- RGBA: يتم تخزين مكونات الأحمر والأخضر والأزرق وألفا في مستوى واحد، عادةً مع 8 بت لكل مكون (32 بت لكل بكسل). يمكن أن يختلف ترتيب المكونات (على سبيل المثال، BGRA).
- RGB565: يتم تخزين مكونات الأحمر والأخضر والأزرق في مستوى واحد مع 5 بت للأحمر و 6 بت للأخضر و 5 بت للأزرق (16 بت لكل بكسل).
- GRAYSCALE: يمثل صور التدرج الرمادي بقيمة إضاءة (سطوع) واحدة لكل بكسل.
ستخبرك خاصية VideoFrame.format بتنسيق البكسل لإطار معين. تأكد من التحقق من هذه الخاصية قبل محاولة الوصول إلى المستويات. يمكنك الرجوع إلى مواصفات WebCodecs للحصول على قائمة كاملة بالتنسيقات المدعومة.
حالات الاستخدام العملي
يفتح الوصول إلى مستويات VideoFrame مجموعة واسعة من الإمكانيات لمعالجة الفيديو المتقدمة في المتصفح. إليك بعض الأمثلة:
1. تأثيرات الفيديو في الوقت الفعلي
يمكنك تطبيق تأثيرات الفيديو في الوقت الفعلي عن طريق معالجة بيانات البكسل في VideoFrame. على سبيل المثال، يمكنك تنفيذ مرشح تدرج رمادي عن طريق حساب متوسط مكونات R و G و B لكل بكسل في إطار RGBA ثم تعيين جميع المكونات الثلاثة إلى تلك القيمة المتوسطة. يمكنك أيضًا إنشاء تأثير درجة اللون البني الداكن (sepia) أو ضبط السطوع والتباين.
async function applyGrayscale(videoFrame) {
const width = videoFrame.codedWidth;
const height = videoFrame.codedHeight;
const buffer = new ArrayBuffer(width * height * 4); // RGBA
const rgba = new Uint8ClampedArray(buffer);
await videoFrame.copyTo(rgba, {
format: 'RGBA',
codedWidth: width,
codedHeight: height
});
for (let i = 0; i < rgba.length; i += 4) {
const r = rgba[i];
const g = rgba[i + 1];
const b = rgba[i + 2];
const gray = (r + g + b) / 3;
rgba[i] = gray; // Red
rgba[i + 1] = gray; // Green
rgba[i + 2] = gray; // Blue
}
// Create a new VideoFrame from the modified data.
const newFrame = new VideoFrame(rgba, {
format: 'RGBA',
codedWidth: width,
codedHeight: height,
timestamp: videoFrame.timestamp,
duration: videoFrame.duration
});
videoFrame.close(); // Release original frame
return newFrame;
}
2. تطبيقات رؤية الحاسوب
توفر مستويات VideoFrame وصولاً مباشرًا إلى بيانات البكسل اللازمة لمهام رؤية الحاسوب. يمكنك استخدام هذه البيانات لتنفيذ خوارزميات للكشف عن الكائنات، والتعرف على الوجه، وتتبع الحركة، والمزيد. يمكنك الاستفادة من WebAssembly للأقسام الحرجة من حيث الأداء في التعليمات البرمجية الخاصة بك.
على سبيل المثال، يمكنك تحويل VideoFrame ملون إلى تدرج رمادي ثم تطبيق خوارزمية للكشف عن الحواف (مثل عامل سوبل) لتحديد الحواف في الصورة. يمكن استخدام هذا كخطوة معالجة مسبقة للتعرف على الكائنات.
3. تحرير الفيديو والتركيب
يمكنك استخدام مستويات VideoFrame لتنفيذ ميزات تحرير الفيديو مثل الاقتصاص، والتحجيم، والتدوير، والتركيب. من خلال معالجة بيانات البكسل مباشرة، يمكنك إنشاء انتقالات وتأثيرات مخصصة.
على سبيل المثال، يمكنك اقتصاص VideoFrame عن طريق نسخ جزء فقط من بيانات البكسل إلى VideoFrame جديد. ستحتاج إلى تعديل إزاحات layout والخطوات (strides) وفقًا لذلك.
4. المرمازات المخصصة وتحويل الترميز
بينما توفر WebCodecs دعمًا مدمجًا للمرمازات الشائعة مثل AV1 و VP9 و H.264، يمكنك أيضًا استخدامها لتنفيذ مرمازات مخصصة أو خطوط أنابيب لتحويل الترميز. ستحتاج إلى التعامل مع عملية الترميز وفك الترميز بنفسك، لكن مستويات VideoFrame تسمح لك بالوصول إلى بيانات البكسل الخام ومعالجتها. قد يكون هذا مفيدًا لتنسيقات الفيديو المتخصصة أو متطلبات الترميز الخاصة.
5. التحليلات المتقدمة
من خلال الوصول إلى بيانات البكسل الأساسية، يمكنك إجراء تحليل عميق لمحتوى الفيديو. يتضمن ذلك مهام مثل قياس متوسط سطوع المشهد، أو تحديد الألوان السائدة، أو اكتشاف التغييرات في محتوى المشهد. يمكن أن يمكّن هذا من تطبيقات تحليلات الفيديو المتقدمة للأمن أو المراقبة أو تحليل المحتوى.
العمل مع Canvas و WebGL
بينما يمكنك معالجة بيانات البكسل مباشرة في مستويات VideoFrame، غالبًا ما تحتاج إلى عرض النتيجة على الشاشة. توفر واجهة CanvasImageBitmap جسرًا بين VideoFrame وعنصر <canvas>. يمكنك إنشاء CanvasImageBitmap من VideoFrame ثم رسمه على لوحة الرسم باستخدام طريقة drawImage().
async function renderVideoFrameToCanvas(videoFrame, canvas) {
const bitmap = await createImageBitmap(videoFrame);
const ctx = canvas.getContext('2d');
ctx.drawImage(bitmap, 0, 0, canvas.width, canvas.height);
bitmap.close(); // Release bitmap resources
videoFrame.close(); // Release VideoFrame resources
}
للحصول على عرض أكثر تقدمًا، يمكنك استخدام WebGL. يمكنك تحميل بيانات البكسل من مستويات VideoFrame إلى مواد WebGL ثم استخدام التظليلات (shaders) لتطبيق التأثيرات والتحويلات. يتيح لك هذا الاستفادة من وحدة معالجة الرسومات (GPU) لمعالجة الفيديو عالية الأداء.
اعتبارات الأداء
يمكن أن يكون العمل مع بيانات البكسل الخام مكثفًا من الناحية الحسابية، لذلك من الضروري مراعاة تحسين الأداء. إليك بعض النصائح:
- تقليل عمليات النسخ: تجنب النسخ غير الضروري لبيانات البكسل. حاول إجراء العمليات في مكانها كلما أمكن ذلك.
- استخدام WebAssembly: بالنسبة للأقسام الحرجة من حيث الأداء في التعليمات البرمجية الخاصة بك، ضع في اعتبارك استخدام WebAssembly. يمكن لـ WebAssembly توفير أداء شبه أصلي للمهام المكثفة حسابيًا.
- تحسين تخطيط الذاكرة: اختر تنسيق البكسل وتخطيط الذاكرة المناسبين لتطبيقك. ضع في اعتبارك استخدام التنسيقات المعبأة (مثل RGBA) إذا لم تكن بحاجة إلى الوصول إلى مكونات الألوان الفردية بشكل متكرر.
- استخدام OffscreenCanvas: للمعالجة في الخلفية، استخدم
OffscreenCanvasلتجنب حظر الخيط الرئيسي. - تحليل أداء التعليمات البرمجية: استخدم أدوات مطوري المتصفح لتحليل أداء التعليمات البرمجية الخاصة بك وتحديد اختناقات الأداء.
توافق المتصفحات
يتم دعم WebCodecs وواجهة برمجة تطبيقات VideoFrame في معظم المتصفحات الحديثة، بما في ذلك Chrome و Firefox و Safari. ومع ذلك، قد يختلف مستوى الدعم اعتمادًا على إصدار المتصفح ونظام التشغيل. تحقق من أحدث جداول توافق المتصفحات على مواقع مثل MDN Web Docs للتأكد من أن الميزات التي تستخدمها مدعومة في المتصفحات المستهدفة. من أجل التوافق عبر المتصفحات، يوصى بالكشف عن الميزات.
المزالق الشائعة واستكشاف الأخطاء وإصلاحها
فيما يلي بعض المزالق الشائعة التي يجب تجنبها عند العمل مع مستويات VideoFrame:
- تخطيط غير صحيح: تأكد من أن مصفوفة
layoutتصف بدقة تخطيط الذاكرة لبيانات البكسل. يمكن أن تؤدي الإزاحات أو الخطوات غير الصحيحة إلى صور تالفة. - تنسيقات بكسل غير متطابقة: تأكد من أن تنسيق البكسل الذي تحدده في طريقة
copyToيطابق التنسيق الفعلي لـVideoFrame. - تسرب الذاكرة: أغلق دائمًا كائنات
VideoFrameوCanvasImageBitmapبعد الانتهاء منها لتحرير الموارد الأساسية. قد يؤدي عدم القيام بذلك إلى تسرب الذاكرة. - العمليات غير المتزامنة: تذكر أن
copyToهي عملية غير متزامنة. استخدمawaitللتأكد من اكتمال عملية النسخ قبل الوصول إلى بيانات البكسل. - القيود الأمنية: كن على دراية بالقيود الأمنية التي قد تنطبق عند الوصول إلى بيانات البكسل من مقاطع الفيديو ذات المصادر المختلفة.
مثال: تحويل YUV إلى RGB
دعونا نفكر في مثال أكثر تعقيدًا: تحويل VideoFrame بتنسيق YUV420 إلى VideoFrame بتنسيق RGB. يتضمن هذا قراءة مستويات Y و U و V، وتحويلها إلى قيم RGB، ثم إنشاء VideoFrame جديد بتنسيق RGB.
يمكن تنفيذ هذا التحويل باستخدام الصيغة التالية:
R = Y + 1.402 * (Cr - 128)
G = Y - 0.34414 * (Cb - 128) - 0.71414 * (Cr - 128)
B = Y + 1.772 * (Cb - 128)
إليك الكود:
async function convertYUV420ToRGBA(videoFrame) {
const width = videoFrame.codedWidth;
const height = videoFrame.codedHeight;
const yPlaneSize = width * height;
const uvPlaneSize = width * height / 4;
const yuvBuffer = new ArrayBuffer(yPlaneSize + 2 * uvPlaneSize);
const yuvPlanes = new Uint8ClampedArray(yuvBuffer);
const layout = [
{ offset: 0, stride: width }, // Y plane
{ offset: yPlaneSize, stride: width / 2 }, // U plane
{ offset: yPlaneSize + uvPlaneSize, stride: width / 2 } // V plane
];
await videoFrame.copyTo(yuvPlanes, {
format: 'I420',
codedWidth: width,
codedHeight: height,
layout: layout
});
const rgbaBuffer = new ArrayBuffer(width * height * 4);
const rgba = new Uint8ClampedArray(rgbaBuffer);
for (let y = 0; y < height; y++) {
for (let x = 0; x < width; x++) {
const yIndex = y * width + x;
const uIndex = Math.floor(y / 2) * (width / 2) + Math.floor(x / 2) + yPlaneSize;
const vIndex = Math.floor(y / 2) * (width / 2) + Math.floor(x / 2) + yPlaneSize + uvPlaneSize;
const Y = yuvPlanes[yIndex];
const U = yuvPlanes[uIndex] - 128;
const V = yuvPlanes[vIndex] - 128;
let R = Y + 1.402 * V;
let G = Y - 0.34414 * U - 0.71414 * V;
let B = Y + 1.772 * U;
R = Math.max(0, Math.min(255, R));
G = Math.max(0, Math.min(255, G));
B = Math.max(0, Math.min(255, B));
const rgbaIndex = y * width * 4 + x * 4;
rgba[rgbaIndex] = R;
rgba[rgbaIndex + 1] = G;
rgba[rgbaIndex + 2] = B;
rgba[rgbaIndex + 3] = 255; // Alpha
}
}
const newFrame = new VideoFrame(rgba, {
format: 'RGBA',
codedWidth: width,
codedHeight: height,
timestamp: videoFrame.timestamp,
duration: videoFrame.duration
});
videoFrame.close(); // Release original frame
return newFrame;
}
يوضح هذا المثال قوة وتعقيد العمل مع مستويات VideoFrame. يتطلب فهمًا جيدًا لتنسيقات البكسل وتخطيط الذاكرة وتحويلات مساحة اللون.
الخاتمة
تفتح واجهة برمجة تطبيقات مستويات VideoFrame في WebCodecs مستوى جديدًا من التحكم في معالجة الفيديو في المتصفح. من خلال فهم كيفية الوصول إلى بيانات البكسل ومعالجتها مباشرة، يمكنك إنشاء تطبيقات متقدمة لتأثيرات الفيديو في الوقت الفعلي، ورؤية الحاسوب، وتحرير الفيديو، والمزيد. على الرغم من أن العمل مع مستويات VideoFrame يمكن أن يكون تحديًا، إلا أن المكافآت المحتملة كبيرة. مع استمرار تطور WebCodecs، ستصبح بلا شك أداة أساسية لمطوري الويب الذين يعملون مع الوسائط.
نشجعك على تجربة واجهة برمجة تطبيقات مستويات VideoFrame واستكشاف قدراتها. من خلال فهم المبادئ الأساسية وتطبيق أفضل الممارسات، يمكنك إنشاء تطبيقات فيديو مبتكرة وعالية الأداء تدفع حدود ما هو ممكن في المتصفح.
لمزيد من التعلم
- مستندات MDN Web Docs حول WebCodecs
- مواصفات WebCodecs
- مستودعات نماذج أكواد WebCodecs على GitHub.